package com.kusej.gitmanger.utils;

import com.google.common.collect.Maps;
import org.apache.commons.lang3.StringUtils;
import org.eclipse.jgit.api.*;
import org.eclipse.jgit.api.errors.GitAPIException;
import org.eclipse.jgit.lib.Ref;
import org.eclipse.jgit.lib.Repository;
import org.eclipse.jgit.lib.RepositoryBuilder;
import org.eclipse.jgit.transport.*;

import java.io.File;
import java.io.IOException;
import java.net.URISyntaxException;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import static com.kusej.gitmanger.utils.Global.*;

public class GitBranchUtils {

    private static final Map<String, Repository> repositoryCache = Maps.newConcurrentMap();

    static {
        Global.init();
    }

    public static List<String> getRemoteTags(String projectPath) throws IOException, URISyntaxException, GitAPIException {
        Repository repository = openRepository(projectPath);
        List<RemoteConfig> remoteConfigs = RemoteConfig.getAllRemoteConfigs(repository.getConfig());
        List<String> remoteBranches = new ArrayList<>();
        for (RemoteConfig remoteConfig : remoteConfigs) {
            LsRemoteCommand lsRemoteCommand = new LsRemoteCommand(repository);
            Collection<Ref> refs = lsRemoteCommand.setRemote(remoteConfig.getName())
                    .setHeads(false)
                    .setTags(true).setTimeout(100)
                    .setCredentialsProvider(provider) // 如果需要认证，可以设置合适的 CredentialsProvider
                    .setTransportConfigCallback(null) // 如果需要设置特定的传输配置，可以设置合适的 TransportConfigCallback
                    .call();
            List<Ref> sortedRefs = new ArrayList<>(refs);
            Pattern pattern = Pattern.compile("\\d+\\.\\d+$|\\d+$");

            sortedRefs.sort((ref1, ref2) -> {
                try {
                    Matcher matcher = pattern.matcher(ref1.getName());
                    double refNum1 =0;
                    if (matcher.find()) {
                        refNum1 = Double.parseDouble(matcher.group());
                    }
                    matcher = pattern.matcher(ref2.getName());
                    double refNum2 =0;
                    if (matcher.find()) {
                        refNum2 = Double.parseDouble(matcher.group());
                    }
                    return refNum2-refNum1>0?1:-1; // 倒序排序
                } catch (Exception e) {
                    e.printStackTrace();
                    return 0;
                }
            });
            for (Ref ref : sortedRefs) {
                String branchName = ref.getName();
                remoteBranches.add(branchName);
            }
        }

        return remoteBranches;
    }

    public static void commitAndPush(String projectPath, String message) throws IOException, GitAPIException {
        Repository repository = openRepository(projectPath);
        Git.wrap(repository).add().addFilepattern(".").call();
        Git.wrap(repository).commit().setMessage(message).call();
        Git.wrap(repository).push().setCredentialsProvider(provider).setRemote(remoteDefault)
                .setRefSpecs(new RefSpec(refHead + repository.getBranch())).call();
    }

    public static void createAndPushTag(String projectPath, String sourceBranch, String tagName, String descText) throws IOException, GitAPIException {
        Repository repository = openRepository(projectPath);
        // 找到最新的提交id
        Ref branchRef = repository.findRef(sourceBranch);
        if (branchRef == null) {
            throw new RuntimeException("分支不存在");
        }
        // 创建标签
        Git.wrap(repository).tag()
                .setName(tagName).setObjectId(repository.parseCommit(branchRef.getObjectId()))
                .setMessage(String.format(Global.descTextFormat, descText, sourceBranch)).setCredentialsProvider(provider)
                .call();

        // 推送至远程
        Git.wrap(repository).push().setCredentialsProvider(provider)
                .setRemote(remoteDefault)
                .setRefSpecs(new RefSpec(refTag + tagName))
                .call();


    }

    public static List<String> getRemoteBranches(String projectPath) throws Exception {
        Repository repository = openRepository(projectPath);
        List<RemoteConfig> remoteConfigs = RemoteConfig.getAllRemoteConfigs(repository.getConfig());
        List<String> remoteBranches = new ArrayList<>();
        for (RemoteConfig remoteConfig : remoteConfigs) {
            LsRemoteCommand lsRemoteCommand = new LsRemoteCommand(repository);
            Collection<Ref> refs = lsRemoteCommand.setRemote(remoteConfig.getName())
                    .setHeads(true)
                    .setTags(false).setTimeout(100)
                    .setCredentialsProvider(provider) // 如果需要认证，可以设置合适的 CredentialsProvider
                    .setTransportConfigCallback(null) // 如果需要设置特定的传输配置，可以设置合适的 TransportConfigCallback
                    .call();
            String currentBranch = repository.getBranch();
            for (Ref ref : refs) {
                String branchName = ref.getName();
                if (branchName.endsWith(currentBranch)) {
                    remoteBranches.add(0, branchName.replace(refHead, remoteFlag));
                } else {
                    remoteBranches.add(branchName.replace(refHead, remoteFlag));
                }
            }
        }

        return remoteBranches;
    }

    public static List<String> getLocalBranches(String projectPath) throws IOException, GitAPIException {
        Repository repository = openRepository(projectPath);
        List<Ref> refs = Git.wrap(repository).branchList().setListMode(ListBranchCommand.ListMode.ALL).call();
        List<String> localBranches = new ArrayList<>();
        String currentBranch = repository.getBranch();

        for (Ref ref : refs) {
            String branchName = ref.getName();
            if (branchName.startsWith(refHead)) {
                if (branchName.endsWith(currentBranch)) {
                    localBranches.add(0, branchName.replace(refHead, ""));
                } else {
                    localBranches.add(branchName.replace(refHead, ""));
                }
//                tagIdMap.put(branchName, ref.getObjectId());

            }
        }

        return localBranches;
    }

    public static void createBranch(String projectPath, String sourceBranch, String newBranch) throws IOException, GitAPIException {
        Repository repository = openRepository(projectPath);

        // 创建分支
        CheckoutCommand checkoutCommand = Git.wrap(repository).checkout().setCreateBranch(true)
                .setName(newBranch);
        if (sourceBranch.startsWith(remoteFlag) || sourceBranch.contains(refTag)) {
            checkoutCommand.setStartPoint(sourceBranch);
        } else {
            sourceBranch = refHead + sourceBranch;
            checkoutCommand.setStartPoint(sourceBranch);
        }
        Ref ref = checkoutCommand.setUpstreamMode(CreateBranchCommand.SetupUpstreamMode.NOTRACK)
                .call();
        Git.wrap(repository).add().addFilepattern(".").call();
        System.out.println("Created branch " + ref.getName());
    }

    public static Repository openRepository(String projectPath) throws IOException {
        if (Objects.nonNull(repositoryCache.get(projectPath))) {
            return repositoryCache.get(projectPath);
        }
        File gitDir = new File(projectPath, ".git");
        if (!gitDir.exists() && !gitDir.isDirectory()) {
            throw new RuntimeException(".git目录不存在，请确认git环境");
        }
        RepositoryBuilder repositoryBuilder = new RepositoryBuilder();
        Repository build = repositoryBuilder.setGitDir(gitDir).readEnvironment().findGitDir().build();
        repositoryCache.put(projectPath, build);
        return build;
    }


    public static void checkout(String projectPath, String targetBranchName) throws IOException, GitAPIException {
        Repository repository = openRepository(projectPath);
        Ref ref = repository.findRef(targetBranchName.replace(remoteFlag, ""));
        if (ref == null) {
            Git.wrap(repository).checkout().setCreateBranch(true).setName(targetBranchName.replace(remoteFlag, "")).setStartPoint(targetBranchName).call();
        } else {
            Git.wrap(repository).checkout().setName(targetBranchName.replace(remoteFlag, "")).call();
        }

    }

    public static void pulls(String projectPath, String... targetBranchNames) throws IOException, GitAPIException {
        Repository repository = openRepository(projectPath);
        for (String branch : targetBranchNames) {
            PullResult call = Git.wrap(repository).pull().setRemote(remoteDefault).setCredentialsProvider(provider).setRemoteBranchName(branch.replace(remoteFlag, "")).call();
            if (!call.isSuccessful()) {
                throw new RuntimeException(call.getMergeResult().getMergeStatus().toString() + ":" + call.getMergeResult().getConflicts().keySet());
            }

        }
    }

    public static void saveOriginUri(String projectPath, String orgUri) throws IOException, URISyntaxException {
        // 获取仓库的配置
        try (Repository repository = openRepository(projectPath)) {
            String oldUri = repository.getConfig().getString("remote", Global.remoteDefault, "url");
            if (StringUtils.isBlank(oldUri)) {
                Git.wrap(repository).remoteAdd()
                        .setName(Global.remoteDefault)   //设置remote名字
                        .setUri(new URIish(orgUri))  //设置url
                        .call();
            } else if (!oldUri.equals(orgUri)) {
                // 两个不相等,先删再建
                Git.wrap(repository).remoteRemove()
                        .setRemoteName(Global.remoteDefault)
                        .call();
                Git.wrap(repository).remoteAdd()
                        .setName(Global.remoteDefault)   //设置remote名字
                        .setUri(new URIish(orgUri))  //设置url
                        .call();
            }
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    public static void fetch(String projectPath, String sourceBranchName) throws GitAPIException, IOException {
        if (sourceBranchName.contains(remoteFlag) || sourceBranchName.contains(refTag)) {
            // 是远程分支，则强制更新
            Repository repository = openRepository(projectPath);
            // 拉取远程标签到本地
            Git.wrap(repository).fetch().setCredentialsProvider(provider)
                    .setRemote(remoteDefault)
                    .setRefSpecs(new RefSpec("+refs/heads/*:refs/remotes/origin/*"))// 待优化，目前强制更新所有分支
//                .setRefSpecs("+"+sourceBranch.replace(remoteFlag, refHead))// +refs/heads/master
                    .call();
        }
    }
}
